---
title: 单例模式
---

单例模式的使用场景是只需要有一个实例存在，比如各种 Mgr 类、各种 Factory 。

## 饿汉式

多数时候，单例模式都是用饿汉式，因为它比较简单。

```java

/**
 * 饿汉式
 * 类加载到内存后，就实例化一个实例，JVM 层面保证线程安全（每个类JVM只会加载一次）
 * 简单实用，推荐使用
 * 唯一缺点：不管用到与否，类装载时就完成实例化
 */
public class Singleton01 {
    private static final Singleton01 INSTANCE = new Singleton01();

    private Singleton01() {
    }

    public static Singleton01 getInstance() {
        return INSTANCE;
    }
}
```

:::tip
单例模式，首先要将构造方法私有化，不允许外部直接创建对象，然后在类内部创建一个实例，再提供一个静态方法，供外部调用。
:::

## 懒汉式

```java

/**
 * lazy loading
 * 虽然达到了按需初始化的目的，但却带来了线程不安全的问题
 */
public class Singleton {
    private static Singleton INSTANCE;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            // 模拟线程运行时长
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Singleton();
        }
        return INSTANCE;
    }
}
```

## 线程安全懒汉式

没有必要写这么复杂，直接用饿汉式就行了。

```java
/**
 * lazy loading
 * 虽然达到了按需初始化的目的，但却带来了线程不安全的问题
 * 可以通过 synchronized 解决，但也带来了效率下降
 * 锁的范围越小越好，串行执行的代码越少越好
 */
public class Singleton {
    // 加上 volatile 关键字，禁止指令重排
    private static volatile Singleton INSTANCE;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                // 模拟线程运行时长
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
```

## 静态内部类

最完美的写法，既实现了懒加载，又实现了线程安全。

```java

/**
 * 静态内部类写法
 * JVM 保证单例，JVM 保证线程安全，加载外部类时不会加载内部类，实现懒加载
 */
public class Singleton {
    private Singleton() {
    }

    private static class SingletonHolder {
        private final static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
```

## 枚举单例

```java
/**
 * Effective Java 第二版推荐写法
 * 不仅可以解决线程安全问题，还可以防止反序列化
 */
public enum Singleton2 {
    INSTANCE;
}
```

:::tip
前面的几种单例，都可以通过反射来破坏单例，只有枚举单例不会被破坏。
反编译之后，枚举是一个 Abstract 类，里面有一个私有的构造方法，所以无法通过反射来创建对象。
:::